﻿using System.Collections.Generic;
using System.Data.Linq.SqlClient;
using System.Linq;
using System.Web.Mvc;
using CodeCamp.Entities01;
using CodeCamp.Models.Sessions;

namespace CodeCamp.Controllers
{
  public class SessionsController : Controller
  {
    internal static string FilterDevSatTag( string sessionName )
    { return sessionName.StartsWith( "#devsat" ) ? sessionName.Substring( 8 ) : sessionName ; }

    #region List action

    // GET: /Sessions/List

    public ActionResult List( string eventName )
    {
      int eventId = EventsHelper.GetEventId( eventName );
      var repository = new CodeCampDataRepository();
      var eventDisplayName = repository.GetEvent( eventId ).DisplayName;

      var eventSessions =
        repository.GetSessions( eventId )
        .Where( s => !SqlMethods.Like( s.Title, "#devsat%" ) )
        .OrderBy( s => s.Title );

      var userPresenter = repository.GetPresenter( eventId, User.Identity.Name );

      // assemble model

      var model = new SessionsListViewModel 
      {
        EventDisplayName = eventDisplayName,

        EventTags = GetEventTags( eventId, repository ),
        Sessions = new List<SessionsListViewModel.Session>(),

        IsUserAuthenticated = User.Identity.IsAuthenticated,
        IsUserAPresenter = userPresenter != null,
        UserPresenterId = userPresenter != null 
          ? userPresenter.Id 
          : 0
      };

      foreach ( var session in eventSessions )
      {
        var sessionTags = new SessionTags
        {
          Tags =
          (
            from st in session.SessionTags
            orderby st.Tag.Name
            select new SessionTags.Tag
            {
              Id = st.TagId,
              Name = st.Tag.Name
            }
          ).ToList()
        };

        model.Sessions.Add(
          new SessionsListViewModel.Session
          {
            SessionId = session.Id,
            Title = session.Title,
            Description = session.Description,
            SessionPresenters = GetSessionPresenters( session ),
            SessionMaterialsUrl = session.SessionMaterialsUrl,
            SessionTags = sessionTags,
            SessionScheduleInfo = GetSessionScheduleInfo( repository, session.Id )
          } );
      }

      return View( "List", model );
    }

    #endregion

    #region Details action

    // GET: /Sessions/Details/5

    public ActionResult Details( int id, string eventName )
    {
      int eventId = EventsHelper.GetEventId( eventName );
      var repository = new CodeCampDataRepository();
      var session = repository.GetSession( id );

      var model = new SessionsDetailsViewModel
      {
        EventDisplayName = repository.GetEvent( eventId ).DisplayName,
        SessionId = id,
        Title = FilterDevSatTag( session.Title ),
        Description = session.Description,
        SessionPresenters = GetSessionPresenters( session ),
        SessionTags = GetSessionTags( session ),
        EventTags = GetEventTags( eventId, repository ),
        SessionMaterialsUrl = session.SessionMaterialsUrl,
        SessionScheduleInfo = GetSessionScheduleInfo( repository, session.Id ),
        IsUserAuthorizedToEdit = IsUserAuthorizedToEditSession( session )
      };

      return View( "Details", model );
    }

    [AcceptVerbs( HttpVerbs.Post )]
    public ActionResult DetailsSetTags( SessionsDetailsViewModel model, int[] sessionTags )
    {
      SetSessionTags( model.SessionId, sessionTags );
      return RedirectToAction( "Details", new { id = model.SessionId } );
    }

    [AcceptVerbs( HttpVerbs.Post )]
    public ActionResult DetailsAddTag( SessionsDetailsViewModel model, string newTagName )
    {
      CreateTagAndAddToSession( model.SessionId, newTagName );
      return RedirectToAction( "Details", new { id = model.SessionId } );
    }

    #endregion

    #region Add action

    // GET: /Sessions/Add

    // TODO: Get this value from the database, and tie it to the eventName
    private static readonly bool isAddEnabled = true;

    [Authorize]
    public ActionResult Add( string eventName )
    {
      int eventId = EventsHelper.GetEventId( eventName );
      var repository = new CodeCampDataRepository();
      var eventDisplayName = repository.GetEvent( eventId ).DisplayName;

      if ( isAddEnabled )
      {
        var presenter = repository.GetPresenter( eventId, User.Identity.Name );
        if ( presenter == null )
          return View( "AuthorizationError" );

        var model =
          new SessionsAddViewModel
          {
            EventDisplayName = eventDisplayName,
            PresenterId = presenter.Id
          };
        return View( "Add", model );
      }
      else
      {
        var model =
          new SessionsAddClosedViewModel
          {
            EventDisplayName = eventDisplayName
          };

        return View( "AddClosed", model );
      }
    }

    // POST: /Sessions/Add

    [AcceptVerbs( HttpVerbs.Post )]
    [Authorize]
    public ActionResult Add( SessionsAddViewModel model, string eventName )
    {
      if ( !isAddEnabled )
        return RedirectToAction( "Add" );

      try
      {
        int eventId = EventsHelper.GetEventId( eventName );

        // add session

        var session = new Session
        {
          EventId = eventId,
          Title = model.Title,
          Description = model.Description,
          SessionMaterialsUrl = model.SessionMaterialsUrl
        };

        var repository = new CodeCampDataRepository();
        repository.Add( session );
        repository.Save();

        // add session presenter

        var sessionPresenter = new SessionPresenter
        {
          PresenterId = model.PresenterId,
          SessionId = session.Id
        };
        repository.Add( sessionPresenter );
        repository.Save();

        return RedirectToAction( "Details", new { id = session.Id } );
      }
      catch
      {
        return View( "Add", model );
      }
    }

    #endregion

    #region Edit action

    // GET: /Sessions/Edit/5

    [Authorize]
    public ActionResult Edit( int id, string eventName )
    {
      int eventId = EventsHelper.GetEventId( eventName );
      var repository = new CodeCampDataRepository();
      var eventDisplayName = repository.GetEvent( eventId ).DisplayName;

      var session = repository.GetSession( id );

      // verify that user is authorized to edit this session
      if ( !IsUserAuthorizedToEditSession( session ) )
        return View( "SessionEditAuthorizationError" );

      var sessionPresenter = session.SessionPresenters.SingleOrDefault();

      var model = new SessionsEditViewModel
      {
        EventDisplayName = eventDisplayName,
        SessionId = session.Id,
        Title = session.Title,
        Description = session.Description,
        SessionMaterialsUrl = session.SessionMaterialsUrl,
        PresenterId = sessionPresenter.PresenterId
      };

      return View( "Edit", model );
    }

    // POST: /Sessions/Edit/5

    [AcceptVerbs( HttpVerbs.Post )]
    [Authorize]
    public ActionResult Edit( SessionsEditViewModel model )
    {
      try
      {
        var repository = new CodeCampDataRepository();
        var session = repository.GetSession( model.SessionId );

        // verify that user is authorized to edit this session
        if ( !IsUserAuthorizedToEditSession( session ) )
          return View( "SessionEditAuthorizationError" );

        session.Title = model.Title;
        session.Description = model.Description;
        session.SessionMaterialsUrl = model.SessionMaterialsUrl;

        var sessionPresenter = session.SessionPresenters.SingleOrDefault();
        sessionPresenter.PresenterId = model.PresenterId;

        repository.Save();

        return RedirectToAction( "Details", new { id = model.SessionId } );
      }
      catch
      {
        return View( "Edit", model );
      }
    }

    #endregion

    #region Tag action

    public ActionResult Tag( int id, string eventName )
    {
      int eventId = EventsHelper.GetEventId( eventName );
      var repository = new CodeCampDataRepository();
      var tag = repository.GetTag( id );
      var taggedSessions =
        from st in tag.SessionTags
        orderby st.Session.Title
        select st.Session;

      var sessionsList = new List<SessionsTagViewModel.Session>();
      foreach ( var taggedSession in taggedSessions )
      {
        sessionsList.Add(
          new SessionsTagViewModel.Session
          {
            Id = taggedSession.Id,
            Title = FilterDevSatTag( taggedSession.Title ),
            Description = taggedSession.Description,
            SessionPresenters = GetSessionPresenters( taggedSession ),
            SessionScheduleInfo = GetSessionScheduleInfo( repository, taggedSession.Id ),
            Tags = GetSessionTags( taggedSession )
          } );
      }

      var model = new SessionsTagViewModel
      {
        EventDisplayName = repository.GetEvent( eventId ).DisplayName,
        ThisTag = new SessionsTagViewModel.Tag { Id = id, Name = tag.Name },
        Sessions = sessionsList,
        EventTags = GetEventTags( eventId, repository )
      };

      return View( "Tag", model );
    }

    #endregion

    #region Helpers

    private bool IsUserAuthorizedToEditSession( Session session )
    {
      var sessionPresenterMatchingUser =
        ( 
          from sp in session.SessionPresenters
          where sp.Presenter.UserIdentityName == User.Identity.Name
          select sp.Presenter
        ).FirstOrDefault();

      return sessionPresenterMatchingUser != null;
    }

    private static EventTags GetEventTags( int eventId, CodeCampDataRepository repository )
    {
      return new EventTags
      {
        Tags =
        (
          from t in repository.GetTags( eventId )
          select new EventTags.Tag
          {
            Id = t.Id,
            Name = t.Name,
            Count = t.SessionTags.Count()
          }
        ).ToList()
      };
    }

    private static SessionTags GetSessionTags( Session session )
    {
      return new SessionTags
      {
        Tags =
        (
          from st in session.SessionTags
          orderby st.Tag.Name
          select new SessionTags.Tag
          {
            Id = st.TagId,
            Name = st.Tag.Name
          }
        ).ToList()
      };
    }

    private static SessionPresenters GetSessionPresenters( Session session )
    {
      return new SessionPresenters
      {
        Presenters =
        (
          from sp in session.SessionPresenters
          orderby sp.Presenter.LastName
          select new SessionPresenters.Presenter
          {
            Id = sp.PresenterId,
            Name = string.Format( "{0} {1}", sp.Presenter.FirstName, sp.Presenter.LastName )
          }
        ).ToList()
      };
    }

    private static void SetSessionTags( int sessionId, int[] sessionTagIds )
    {
      var repository = new CodeCampDataRepository();
      var currentSessionTags = repository.GetSessionTags( sessionId ).ToList();

      if ( sessionTagIds != null )
      {
        foreach ( var tagId in sessionTagIds )
        {
          int index = currentSessionTags.FindIndex( t => t.TagId == tagId );
          if ( index == -1 )      // tagId not in current session tags
            repository.Add( new SessionTag { SessionId = sessionId, TagId = tagId } );
          else                    // tagId in current session tags
            currentSessionTags.RemoveAt( index );
        }
      }
      foreach ( var currentSessionTag in currentSessionTags )
        repository.Delete( currentSessionTag );

      repository.Save();
    }

    private static void CreateTagAndAddToSession( int sessionId, string newTagName )
    {
      if ( !string.IsNullOrEmpty( newTagName ) ) // valid tag name
      {
        newTagName = newTagName.Trim();
        if ( !string.IsNullOrEmpty( newTagName ) ) // tag not all whitespace
        {
          var repository = new CodeCampDataRepository();
          int eventId = repository.GetSession( sessionId ).EventId;

          if ( repository.GetTag( eventId, newTagName ) == null ) // tag doesn't exist
          {
            // create tag
            var tag = new Tag { EventId = eventId, Name = newTagName };
            repository.Add( tag );
            repository.Save();

            AddTagToSession( sessionId, tag.Id, repository );
          }
        }
      }
    }

    private static void AddTagToSession( int sessionId, int tagId, CodeCampDataRepository repository )
    {
      repository.Add( new SessionTag { SessionId = sessionId, TagId = tagId } );
      repository.Save();
    }

    private static SessionScheduleInfo GetSessionScheduleInfo( CodeCampDataRepository repository, int sessionId )
    {
      SessionScheduleInfo sessionScheduleInfo = null;
      var scheduledSession = repository.GetScheduledSession( sessionId );
      if ( scheduledSession != null )
      {
        var room = scheduledSession.Room;
        var timeslot = scheduledSession.Timeslot;

        sessionScheduleInfo = new SessionScheduleInfo
        {
          Building = room.Building.Name,
          Room = room.Name,
          RoomId = room.Id,
          Date = timeslot.Date,
          StartTime = DateTimeHelper.CombineDateAndTime( timeslot.Date, timeslot.StartTime ),
          EndTime = DateTimeHelper.CombineDateAndTime( timeslot.Date, timeslot.EndTime )
        };
      }
      return sessionScheduleInfo;
    }

    #endregion
  }
}
